package cn.cloud.all.security.oauth2.provider.code;

import cn.cloud.all.security.oauth2.provider.OAuth2Authentication;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.SqlLobValue;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class JdbcAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {

    private static final String DEFAULT_SELECT_STATEMENT = "SELECT code, authentication FROM oauth_code WHERE code = ?";
    private static final String DEFAULT_INSERT_STATEMENT = "INSERT INTO oauth_code (code, authentication) VALUES (?, ?)";
    private static final String DEFAULT_DELETE_STATEMENT = "DELETE FROM oauth_code WHERE code = ?";

    private final JdbcTemplate jdbcTemplate;

    public JdbcAuthorizationCodeServices(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    protected void store(String code, OAuth2Authentication authentication) {
        jdbcTemplate.update(DEFAULT_INSERT_STATEMENT, new Object[]{code, new SqlLobValue(SerializationUtils.serialize(authentication))}, new int[]{Types.VARCHAR, Types.BLOB});
    }

    public OAuth2Authentication remove(String code) {
        OAuth2Authentication authentication;
        try {
            authentication = jdbcTemplate.queryForObject(DEFAULT_SELECT_STATEMENT, new RowMapper<OAuth2Authentication>() {
                public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return SerializationUtils.deserialize(rs.getBytes("authentication"));
                }
            }, code);
        } catch (EmptyResultDataAccessException e) {
            return null;
        }

        if (authentication != null) {
            jdbcTemplate.update(DEFAULT_DELETE_STATEMENT, code);
        }

        return authentication;
    }
}
